//!
/**
 **************************************************************************************************************************************************************
 * <RBRenderer Implementation>
 **************************************************************************************************************************************************************
 * @file RBRenderer.cpp
 **************************************************************************************************************************************************************/

/**************************************************************************************************************************************************************
 * Precompiled Header
 **************************************************************************************************************************************************************/
#include "RBMainPCH.h"

/**************************************************************************************************************************************************************
 * Includes
 **************************************************************************************************************************************************************/
#include "RBRenderer.h"

#include "RBCamera.h"
#include "RBGeometry.h"
#include "RBHardwareBufferManager.h"
#include "RBIndexData.h"
#include "RBMaterial.h"
#include "RBRenderTargetGroup.h"
#include "RBTextureSampler.h"

#include "RBRenderProcess/RBEffect.h"
#include "RBRenderProcess/RBEffectFactory.h"
#include "RBRenderProcess/RBEffectPass.h"
#include "RBRenderProcess/RBEffectTechnique.h"

/**************************************************************************************************************************************************************
 * Defines
 **************************************************************************************************************************************************************/

/**************************************************************************************************************************************************************
 * Classes implementation
 **************************************************************************************************************************************************************/

/**
 **************************************************************************************************************************************************************
 * Constructor
 **************************************************************************************************************************************************************/
RBRenderer::RBRenderer()
: m_pViewCamera(NULL)
, m_pBackBuffer(NULL)
, m_pDepthStencil(NULL)
, m_default_target_group(NULL)
, m_colorWriteEnable(UINT_MAX)
, m_prev_raster(NULL)
, m_prev_stencil_state(NULL)
, m_prev_blend_state(NULL)
, m_prev_idx_buffer(NULL)
, m_prev_vtx_buffer(NULL)
, m_prev_vtx_decl(NULL)
, m_prev_pt(0xFFFFFFFF)
{
    new RBEffectFactory;
    // 32 K Allocated for constant data: should be enough
    m_pShaderConstantData = malloc(1024*32);
    m_ppViews = new const RBShaderResourceView*[ms_uiMaxShaderResources];
    m_ppSamplerStates = new const RBShaderSamplerState*[ms_uiMaxSamplers];

    m_ProjOffsetScale = float4x4::Identity();

}

/**
 **************************************************************************************************************************************************************
 * Destructor
 **************************************************************************************************************************************************************/
RBRenderer::~RBRenderer()
{
    delete m_default_target_group;
    delete[] m_ppSamplerStates;
    delete[] m_ppViews;
    free(m_pShaderConstantData);
    delete RBEffectFactory::GetSingleton();
}

/**************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************/
void RBRenderer::Render(RBHandle<RBGeometry> pGeometry)
{
    Draw(&RBGeometry::Get(pGeometry));
}

/**************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************/
void RBRenderer::Draw(const RBGeometry* pGeometry)
{
    PROFILE_FUNC();
    RBMaterial* pMaterial = pGeometry->GetMaterial();
    RBEffect* pEffect = pMaterial->GetEffect();
    uint32 uiTechnique = pMaterial->GetTechnique();
    RBEffectTechnique* pTechnique = pEffect->GetTechnique(uiTechnique);
    uint32 uiPrimitiveCount = pGeometry->VertexCount;
    RBGeometryPrimitiveType::EType ePT = pGeometry->GetPrimitiveType();

    {
        PROFILE_BLOCK(Prepare_Render_data);
        // Todo: set default render states
        pEffect->BuildEffectBuffer(this, pGeometry, pMaterial, m_pShaderConstantData, m_ppViews, m_ppSamplerStates, RBEffectFactory::GetSingleton());

        {
            PROFILE_BLOCK(Prepare_States);
            RBRasterizerState* cur_raster_state = RBEffect::FindRasterState(*pMaterial);
            if (m_prev_raster != cur_raster_state)
            {
                RSSetState(cur_raster_state);
                m_prev_raster = cur_raster_state;
            }
            
            RBDepthStencilState* cur_stencil_state = RBEffect::FindDepthStencilState(*pMaterial);
            if (m_prev_stencil_state != cur_stencil_state)
            {
                OMSetDepthStencilState(cur_stencil_state);
                m_prev_stencil_state = cur_stencil_state;
            }
            

            RBBlendState* cur_blend_state = RBEffect::FindBlendState(*pMaterial, m_colorWriteEnable);
            if (m_prev_blend_state != cur_blend_state)
            {
                OMSetBlendState(cur_blend_state);
                m_prev_blend_state = cur_blend_state;
            }

            // Setup Buffers 
            if (m_prev_vtx_decl != pGeometry->VertexDeclaration)
            {
                IASetVertexDecl(pGeometry->VertexDeclaration);
                m_prev_vtx_decl = pGeometry->VertexDeclaration;
            }
            
            if (m_prev_vtx_buffer != pGeometry->VertexBuffer)
            {
                IASetVertexBuffer(0, pGeometry->VertexBuffer);
                m_prev_vtx_buffer = pGeometry->VertexBuffer;
            }
            

            if (pGeometry->IndexBuffer)
            {
                if (m_prev_idx_buffer != pGeometry->IndexBuffer)
                {
                    IASetIndexBuffer(pGeometry->IndexBuffer);
                    m_prev_idx_buffer = pGeometry->IndexBuffer;
                }
            }

            if (m_prev_pt != ePT)
            {
                IASetTopology(ePT);
                m_prev_pt = ePT;
            }
            
        }


        switch(ePT)
        {
        case RBGeometryPrimitiveType::POINTLIST:
            break;
        case RBGeometryPrimitiveType::LINELIST:
            uiPrimitiveCount = uiPrimitiveCount / 2;
            break;
        case RBGeometryPrimitiveType::LINESTRIP:
            uiPrimitiveCount = uiPrimitiveCount - 1;
            break;
        case RBGeometryPrimitiveType::TRIANGLELIST:
            uiPrimitiveCount = uiPrimitiveCount / 3;
            break;
        case RBGeometryPrimitiveType::TRIANGLESTRIP:
            uiPrimitiveCount = uiPrimitiveCount - 2;
            break;
        default:
            RBAssert(false, "Unknown Primitive Type");
            return;
        }
    }

    {
        PROFILE_BLOCK(Prepare_Draw);
        uint32 uiPassCount = pTechnique->GetPassCount();
        for (uint32 uiPass=0; uiPass < uiPassCount; ++uiPass)
        {
			{
				PROFILE_BLOCK(begin_draw);
				pTechnique->GetPass(uiPass)->Begin(this, m_pShaderConstantData, m_ppViews, m_ppSamplerStates);
			}

			{
				PROFILE_BLOCK(draw);
				if (pGeometry->IndexCount > 0)
				{
                    DrawIndexedPrimitive(pGeometry->VertexCount, uiPrimitiveCount, pGeometry->VertexStart, pGeometry->IndexStart );
				}
				else
				{
					DrawPrimitive(uiPrimitiveCount, 0 );
				}
			}

			{
				PROFILE_BLOCK(end_draw);
				pTechnique->GetPass(uiPass)->End(this);
			}
        }
    }

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBRenderer::UpdateCamera(RBCamera* camera)
{
    camera = camera;

    m_View = camera->GetViewMatrix();
    
    m_Proj = BuildProjMatrix(camera);

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
bool RBRenderer::Init()
{
    m_pHBMgr = CreateHBMgr();

    return true;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBRenderer::Release()
{
    delete m_pHBMgr;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
RBRenderTargetView* RBRenderer::GetBackBuffer()
{
    return m_pBackBuffer;
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
RBDepthStencilView* RBRenderer::GetDepthStencil()
{
    return m_pDepthStencil;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
uint32 RBRenderer::AddResetFunction(ResetFunction func)
{
    uint32 uiRet = m_vResetFunctions.size();
    m_vResetFunctions.push_back(func);
    return uiRet;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBRenderer::RemoveResetFunction(uint32 ui)
{
    m_vResetFunctions.erase(m_vResetFunctions.begin() + ui);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
bool RBRenderer::RestoreLostDevice( bool /*bForceReset*/ /*= false*/ )
{
    RBEffectFactory::GetSingleton()->Reset(true);
    RBEffectFactory::GetSingleton()->Reset(false);
    return true;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBRenderTargetGroup* RBRenderer::GetDefaultGroup()
{
    return m_default_target_group;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBRenderer::SetColorWrite( uint32 i, uint8 mask )
{
    uint32 clearVal = ~((COLOR_WRITE_ALL)<<(i*4));
    uint32 maskInBitfield = (mask&COLOR_WRITE_ALL) << (i*4);
    m_colorWriteEnable = m_colorWriteEnable & clearVal | maskInBitfield;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
float4x4 RBRenderer::BuildProjMatrix( RBCamera* camera )
{
    return camera->HasCustomProj() ? camera->GetProjMatrix() : m_ProjOffsetScale * camera->GetProjMatrix();
}

void RBRenderer::Lock( RBTexture* texture, uint32 mapType, rb_locked_data* pMapped, uint32 sub_resource )
{
    Map(texture->GetNative(), mapType, pMapped, sub_resource);
}

void RBRenderer::Unlock( RBTexture* texture, uint32 sub_resource )
{
    Unmap(texture->GetNative(), sub_resource);
}

void RBRenderer::clear_state()
{
    m_prev_raster = NULL;
    m_prev_stencil_state = NULL;
    m_prev_blend_state = NULL;
    m_prev_idx_buffer = NULL;
    m_prev_vtx_buffer = NULL;
    m_prev_vtx_decl = NULL;
    m_prev_pt = 0xFFFFFFFF;

    ClearState();
}



/**************************************************************************************************************************************************************/
